﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;
using System.Threading;
using LDSBusiness.EventArgs;
using LDSCore;
using LDSModel;
using LDSBusiness;
using LDSModel.Dal;
using LDSProject.Properties;

namespace LDSProject.Controls
{
    public partial class ChartControl : UserControl
    {
        Guid _deviceId;

        DateTime _startTime;
        List<Data_Records> _recordList;
        ChartQueryType _type;

        bool _exitAutoFillPointThread;

        public ChartControl()
        {
            InitializeComponent();

            Disposed += ChartControl_Disposed;
        }

        void ChartControl_Disposed(object sender, EventArgs e)
        {
            MainConsole.Instance.OnRecordReceived -= DataService_OnRecordReceived;

            _exitAutoFillPointThread = true;
        }

        private void ChartControl_Load(object sender, EventArgs e)
        {
            try
            {
                foreach (var area in chart1.ChartAreas)
                {
                    area.AxisY.Title = "电流值（kA）";
                    area.AxisX.ArrowStyle = AxisArrowStyle.Triangle;
                    area.AxisX.MajorGrid.LineDashStyle = ChartDashStyle.Dot;
                    area.AxisY.ArrowStyle = AxisArrowStyle.Triangle;
                    area.AxisY.MajorGrid.LineDashStyle = ChartDashStyle.Dot;
                    area.AxisX.LabelStyle.Format = "G";
                    area.CursorX.IsUserEnabled = true;
                    area.CursorX.IntervalType = DateTimeIntervalType.Seconds;
                }

                foreach (var serie in chart1.Series)
                {
                    serie.XValueType = ChartValueType.DateTime;
                }

                MainConsole.Instance.OnRecordReceived += DataService_OnRecordReceived;
            }
            catch (Exception ex)
            {
                LogFileHelper.Record(ex);
            }
        }

        void DataService_OnRecordReceived(object sender, RecordReceivedEventArgs args)
        {
            if (_type != ChartQueryType.Now || args.Record.DeviceID != _deviceId) return;
            if (!IsDisposed)
                Invoke(new DelegateAppendChartPoint(AppendRecord), args.Record, true);
        }

        /// <summary>
        /// 重新构造图表数据
        /// </summary>
        /// <param name="deviceId">设备Id</param>
        /// <param name="type">查询类型</param>
        /// <param name="startTime">开始时间</param>
        /// <param name="endTime">结束时间</param>
        public void ReLoad(Guid deviceId, ChartQueryType type, DateTime startTime, DateTime endTime)
        {
            _deviceId = deviceId;
            _type = type;

            try
            {
                foreach (var area in chart1.ChartAreas)
                {
                    while (area.AxisX.ScaleView.IsZoomed)
                        area.AxisX.ScaleView.ZoomReset();
                }

                if (type == ChartQueryType.Now)
                {
                    startTime = DateTime.Now.AddMinutes(-Settings.Default.QueryNowMinute);
                    startTime = new DateTime(startTime.Year, startTime.Month, startTime.Day, startTime.Hour, startTime.Minute, startTime.Second - (startTime.Second % Settings.Default.QueryPointDelaySecond));
                    endTime = DateTime.Now;

                    ThreadExecutor.Execute(AutoFillPoint);

                    foreach (var area in chart1.ChartAreas)
                    {
                        area.CursorX.IsUserSelectionEnabled = false;

                        area.AxisX.Minimum = double.NaN;
                        area.AxisX.Maximum = double.NaN;
                    }
                }
                else
                {
                    _exitAutoFillPointThread = true;

                    foreach (var area in chart1.ChartAreas)
                    {
                        area.CursorX.IsUserSelectionEnabled = true;

                        area.AxisX.Minimum = startTime.ToOADate();
                        area.AxisX.Maximum = endTime.ToOADate();
                    }
                }

                _startTime = startTime;
                _recordList = RecordService.Query(deviceId, startTime, endTime);

                if (type == ChartQueryType.Now)
                {
                    for (var time = startTime.AddSeconds(-startTime.Second % Settings.Default.QueryPointDelaySecond); time < endTime; time = time.AddSeconds(Settings.Default.QueryPointDelaySecond))
                    {
                        if (null == _recordList.Find(r => r.AlarmTime == time))
                        {
                            _recordList.Add(new Data_Records
                                {
                                    CH1Value = 0,
                                    CH2Value = 0,
                                    AlarmTime = time
                                });
                        }
                    }
                }
                //else if (_recordList.Count > 0)
                //{
                //    var curRecord = _recordList[0];
                //    var rList = new List<Data_Records>(_recordList);
                //    foreach (var record in rList)
                //    {
                //        if (curRecord.AlarmTime != null && (record.AlarmTime != null && record.AlarmTime.Value.Subtract(curRecord.AlarmTime.Value).TotalMinutes > Settings.Default.ChartDelayTime))
                //        {
                //            _recordList.Add(new Data_Records
                //                {
                //                    CH1Value = 0,
                //                    CH2Value = 0,
                //                    AlarmTime = curRecord.AlarmTime.Value.AddMilliseconds(1)
                //                });
                //            _recordList.Add(new Data_Records
                //                {
                //                    CH1Value = 0,
                //                    CH2Value = 0,
                //                    AlarmTime = record.AlarmTime.Value.AddMilliseconds(-1)
                //                });
                //        }

                //        curRecord = record;
                //    }
                //}

                chart1.Annotations[0].Visible = false;

                Refush(type);

            }
            catch (Exception ex)
            {
                LogFileHelper.Record(ex);
            }
        }

        void AutoFillPoint(object state)
        {
            while (true)
            {
                if (_exitAutoFillPointThread)
                {
                    _exitAutoFillPointThread = false;
                    return;
                }

                try
                {
                    if (DateTime.Now.Second % Settings.Default.QueryPointDelaySecond == 0)
                    {
                        var record = new Data_Records { AlarmTime = DateTime.Now, CH1Value = 0, CH2Value = 0 };
                        if (!IsDisposed)
                            Invoke(new DelegateAppendChartPoint(AppendRecord), record, false);
                    }
                }
                catch (Exception ex)
                {
                    LogFileHelper.Record(ex);
                }
                Thread.Sleep(1000);
            }
        }

        void Refush(ChartQueryType type)
        {
            try
            {
                if (_recordList.Count > 0)
                {
                    _recordList.Sort((r1, r2) => r1.AlarmTime != null ? r1.AlarmTime.Value.CompareTo(r2.AlarmTime) : 0);

                    foreach (var series in chart1.Series)
                    {
                        series.Points.Clear();
                    }

                    var curRecord = _recordList[0];
                    foreach (var record in _recordList)
                    {
                        var pointCh1 = new DataPoint(record.AlarmTime.Value.ToOADate(), (double)record.CH1Value.Value);
                        var pointCh2 = new DataPoint(record.AlarmTime.Value.ToOADate(), (double)record.CH2Value.Value);

                        if (type == ChartQueryType.History)
                        {
                            if (curRecord.AlarmTime != null &&
                                (record.AlarmTime != null &&
                                 record.AlarmTime.Value.Subtract(curRecord.AlarmTime.Value).TotalMinutes >
                                 Settings.Default.ChartDelayTime))
                            {
                                pointCh1.Color = Color.FromArgb(0, 0, 0, 0);
                                pointCh1.IsValueShownAsLabel = true;

                                pointCh2.Color = Color.FromArgb(0, 0, 0, 0);
                                pointCh2.IsValueShownAsLabel = true;
                            }
                        }

                        chart1.Series[0].Points.Add(pointCh1);
                        chart1.Series[1].Points.Add(pointCh2);

                        curRecord = record;
                    }

                    var maxValuePoint = chart1.Series[0].Points.FindMaxByValue();
                    if (maxValuePoint != null)
                    {
                        maxValuePoint.LabelForeColor = Color.FromArgb(255, 0, 0);
                        maxValuePoint.IsValueShownAsLabel = true;
                    }

                    var minValuePoint = chart1.Series[0].Points.FindMinByValue();
                    if (minValuePoint != null)
                    {
                        minValuePoint.LabelForeColor = Color.FromArgb(0, 0, 255);
                        minValuePoint.IsValueShownAsLabel = true;
                    }

                    maxValuePoint = chart1.Series[1].Points.FindMaxByValue();
                    if (maxValuePoint != null)
                    {
                        maxValuePoint.LabelForeColor = Color.FromArgb(255, 0, 0);
                        maxValuePoint.IsValueShownAsLabel = true;
                    }

                    minValuePoint = chart1.Series[1].Points.FindMinByValue();
                    if (minValuePoint != null)
                    {
                        minValuePoint.LabelForeColor = Color.FromArgb(0, 0, 255);
                        minValuePoint.IsValueShownAsLabel = true;
                    }

                    if (type == ChartQueryType.History)
                    {
                        chart1.Series[0].Points[0].Label = DateTime.FromOADate(chart1.Series[0].Points[0].XValue).ToString(CultureInfo.InvariantCulture);
                        chart1.Series[1].Points[0].Label = DateTime.FromOADate(chart1.Series[1].Points[0].XValue).ToString(CultureInfo.InvariantCulture);
                    }
                }
                else
                {
                    _recordList.Add(new Data_Records { AlarmTime = _startTime, CH1Value = 0, CH2Value = 0 });

                    chart1.Series[0].Points.DataBindXY(_recordList, "AlarmTime", _recordList, "CH1Value");
                    chart1.Series[1].Points.DataBindXY(_recordList, "AlarmTime", _recordList, "CH2Value");

                    MessageBox.Show(this, @"未查询到雷击记录.", @"提示");
                }
            }
            catch (Exception ex)
            {
                LogFileHelper.Record(ex);
            }


            //chart1.Focus();

            //string formula = "AVE, X:CENTER";
            //chart1.DataManipulator.Group(formula, 1, intervaltype,
            //"S1", "端口A");
            //chart1.DataManipulator.Group(formula, 1, intervaltype,
            //"S2", "端口B");
        }

        void AppendRecord(Data_Records record, bool rewrite)
        {
            if (record.AlarmTime != null && DateTime.Now.Subtract(record.AlarmTime.Value).TotalSeconds > Settings.Default.QueryNowMinute * 60)
                return;

            if (null == _recordList)
                _recordList = new List<Data_Records>();

            try
            {
                var tRecord = _recordList.FirstOrDefault(r => r.AlarmTime == record.AlarmTime);

                if (null != tRecord)
                {
                    if (rewrite)
                    {
                        _recordList.Remove(tRecord);
                    }
                }
                _recordList.Add(record);

                while (true)
                {
                    var time = _recordList[0].AlarmTime;
                    if (time != null && DateTime.Now.Subtract(time.Value).TotalSeconds > Settings.Default.QueryNowMinute * 60)
                        _recordList.RemoveAt(0);
                    else
                        break;
                }

                Refush(ChartQueryType.Now);
            }
            catch (Exception ex)
            {
                LogFileHelper.Record(ex);
            }
        }

        private void chart1_CursorPositionChanging(object sender, CursorEventArgs e)
        {
            if (double.IsNaN(e.NewPosition))
                return;

            if (e.Axis.AxisName == AxisName.X)
            {
                SetCursorY(e.NewPosition);
            }
        }

        private void SetCursorY(double position)
        {
            try
            {
                var point = (from p in chart1.Series[0].Points where p.XValue == position select p).FirstOrDefault();

                if (null != point)
                {
                    chart1.ChartAreas[0].CursorY.Position = point.YValues[0];

                    point = (from p in chart1.Series[1].Points where p.XValue == position select p).FirstOrDefault();

                    if (null != point)
                    {
                        chart1.ChartAreas[1].CursorY.Position = point.YValues[0];
                    }
                    if (point != null)
                        ((RectangleAnnotation)chart1.Annotations[0]).Text = string.Format("时间:\t{0}\nA端口电流值:\t{1}kA\nB端口电流值:\t{2}kA",
                                                                                          DateTime.FromOADate(point.XValue).ToString(CultureInfo.InvariantCulture),
                                                                                          chart1.ChartAreas[0].CursorY.Position.ToString("0.000"),
                                                                                          chart1.ChartAreas[1].CursorY.Position.ToString("0.000"));
                    chart1.Annotations[0].Visible = true;
                }
                else
                {
                    chart1.ChartAreas[0].CursorY.Position = 200;
                    chart1.ChartAreas[1].CursorY.Position = 200;
                    chart1.Annotations[0].Visible = false;
                }
            }
            catch (Exception ex)
            {
                LogFileHelper.Record(ex);
            }
        }

        //private void SetView()
        //{
        //    foreach (ChartArea area in chart1.ChartAreas)
        //    {
        //        // Keep the cursor from leaving the max and min axis points
        //        if (area.CursorX.Position < 0)
        //            area.CursorX.Position = 0;

        //        else if (area.CursorX.Position > 75)
        //            area.CursorX.Position = 75;

        //        // Move the view to keep the cursor visible
        //        if (area.CursorX.Position < area.AxisX.ScaleView.Position)
        //            area.AxisX.ScaleView.Position = area.CursorX.Position;

        //        else if ((area.CursorX.Position > (area.AxisX.ScaleView.Position + area.AxisX.ScaleView.Size)))
        //        {
        //            area.AxisX.ScaleView.Position =
        //                (area.CursorX.Position - area.AxisX.ScaleView.Size);
        //        }
        //    }
        //}


        private void chart1_KeyUp(object sender, KeyEventArgs e)
        {
            try
            {
                DataPoint point;

                if (e.KeyCode == Keys.Right)
                {
                    point = (from p in chart1.Series[0].Points
                             where p.XValue > chart1.ChartAreas[0].CursorX.Position
                             orderby p.XValue
                             select p).FirstOrDefault();

                    if (null != point)
                        chart1.ChartAreas[0].CursorX.Position = DateTime.FromOADate(point.XValue).ToOADate();
                }
                else if (e.KeyCode == Keys.Left)
                {
                    point = (from p in chart1.Series[0].Points
                             where p.XValue < chart1.ChartAreas[0].CursorX.Position
                             orderby p.XValue descending
                             select p).FirstOrDefault();

                    if (null != point)
                        chart1.ChartAreas[0].CursorX.Position = DateTime.FromOADate(point.XValue).ToOADate();
                }
                SetCursorY(chart1.ChartAreas[0].CursorX.Position);
            }
            catch (Exception ex)
            {
                LogFileHelper.Record(ex);
            }
        }

        private void chart1_MouseClick(object sender, MouseEventArgs e)
        {
            HitTestResult hitResult = chart1.HitTest(e.X, e.Y);
            if (hitResult.ChartElementType == ChartElementType.Nothing)
            {
                chart1.Annotations[0].Visible = false;

                chart1.ChartAreas[0].CursorX.Position = 0;
                chart1.ChartAreas[0].CursorY.Position = 400;
                chart1.ChartAreas[1].CursorX.Position = 0;
                chart1.ChartAreas[1].CursorY.Position = 400;
            }
        }

        private void chart1_Click(object sender, EventArgs e)
        {
            chart1.Focus();
        }

        private void ckPortA_CheckedChanged(object sender, EventArgs e)
        {
            chart1.ChartAreas[0].Visible = ckPortA.Checked;
            chart1.Series[0].IsVisibleInLegend = ckPortA.Checked;
        }

        private void ckPortB_CheckedChanged(object sender, EventArgs e)
        {
            chart1.ChartAreas[1].Visible = ckPortB.Checked;
            chart1.Series[1].IsVisibleInLegend = ckPortB.Checked;
        }
    }
}
